﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using AIStudio.Wpf.DiagramDesigner.Serializable.ViewModels;
using Newtonsoft.Json;


namespace AIStudio.Wpf.DiagramDesigner.Serializable
{
    public static class DiagramDataExtention
    {
        #region ToJson
        public static string ToJson(this IDiagramViewModel diagram)
        {
            var json = JsonConvert.SerializeObject(new {
                Nodes = diagram.Items.OfType<DiagramItemViewModel>().Select(p => p.ToDiagramNode()).Where(p => p != null),
                Links = diagram.Items.OfType<ConnectionViewModel>().Select(p => p.ToDiagramLink()).Where(p => p != null)
            }, new JsonSerializerSettings
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            });

            return json;
        }

        public static DiagramNode ToDiagramNode(this DiagramItemViewModel nodeModel)
        {
            DiagramNode diagramNode = nodeModel.ToDiagram();

            diagramNode.Id = nodeModel.Id.ToString();
            if (nodeModel.ParentId != Guid.Empty)
            {
                diagramNode.ParentId = nodeModel.ParentId.ToString();
            }

            diagramNode.Color = SerializeHelper.SerializeColor(nodeModel.ColorViewModel.FillColor.Color);
            diagramNode.LineColor = SerializeHelper.SerializeColor(nodeModel.ColorViewModel.LineColor.Color);
            diagramNode.FontColor = SerializeHelper.SerializeColor(nodeModel.FontViewModel.FontColor);
            diagramNode.FontSize = nodeModel.FontViewModel.FontSize;
            diagramNode.CornerRadius = SerializeHelper.SerializeCornerRadius(nodeModel.CornerRadius);
            diagramNode.BorderThickness = SerializeHelper.SerializeThickness(nodeModel.BorderThickness);

            diagramNode.Name = nodeModel.Name;
            diagramNode.Label = nodeModel.Text;
            diagramNode.Width = nodeModel.ItemWidth * ScreenHelper.ScreenScale;
            diagramNode.Height = nodeModel.ItemHeight * ScreenHelper.ScreenScale;
            diagramNode.X = nodeModel.Left * ScreenHelper.ScreenScale;
            diagramNode.Y = nodeModel.Top * ScreenHelper.ScreenScale;
            diagramNode.ZIndex = nodeModel.ZIndex;
            diagramNode.Type = diagramNode.GetType().Name;
            diagramNode.PortAlignmentList = nodeModel.Connectors.Select(p => p.Orientation.ToString()).ToList();
            diagramNode.IsInnerConnector = nodeModel.IsInnerConnector;
            diagramNode.PortRatio = nodeModel.Connectors.Select(p => SerializeHelper.SerializePoint(p.Ratio)).ToList();
            return diagramNode;
        }

        public static DiagramLink ToDiagramLink(this ConnectionViewModel linkModel)
        {
            DiagramLink diagramLink = new DiagramLink();

            diagramLink.Id = linkModel.Id.ToString();
            diagramLink.Color = SerializeHelper.SerializeColor(linkModel.ColorViewModel.LineColor.Color);
            diagramLink.SelectedColor = SerializeHelper.SerializeColor(Colors.Black);
            diagramLink.Width = linkModel.ColorViewModel.LineWidth;
            diagramLink.Label = linkModel.Text;
            diagramLink.ZIndex = linkModel.ZIndex;

            if (linkModel.IsFullConnection) 
            {
                diagramLink.SourceId = linkModel.SourceConnectorInfoFully.DataItem.Id.ToString();
                diagramLink.TargetId = linkModel.SinkConnectorInfoFully.DataItem.Id.ToString();

                diagramLink.Router = linkModel.RouterMode;
                diagramLink.PathGenerator = linkModel.PathMode;
                diagramLink.SourceMarkerPath = linkModel.ShapeViewModel.SourceMarker.Path;
                diagramLink.SourceMarkerWidth = linkModel.ShapeViewModel.SourceMarker.Width;
                diagramLink.TargetMarkerPath = linkModel.ShapeViewModel.SinkMarker.Path;
                diagramLink.TargetMarkerWidth = linkModel.ShapeViewModel.SinkMarker.Width;

                diagramLink.Type = diagramLink.GetType().Name;

                diagramLink.SourcePortAlignment = linkModel.SourceConnectorInfoFully.Orientation.ToString();
                diagramLink.TargetPortAlignment = linkModel.SinkConnectorInfoFully.Orientation.ToString();
            }
            else//Todo,半连接线也可反序列化
            {
                return null;
            }
            return diagramLink;
        }
        #endregion

        #region ToObject
        public static void ToObject(this IDiagramViewModel diagram, string json)
        {
            var data = JsonConvert.DeserializeObject<DiagramData>(json, new JsonConverter[] { new DiagramNodeConverter(), new DiagramLinkConverter() });
            if (data != null)
            {
                ToObject(diagram, data);
            }
        }
        public static void ToObject(this IDiagramViewModel diagram, DiagramData data)
        {
            diagram.Items.Clear();

            List<DiagramItemViewModel> nodes = new List<DiagramItemViewModel>();
            if (data.Nodes != null)
            {
                foreach (var node in data.Nodes)
                {
                    var nodemodel = node.ToNodelModel(diagram);
                    nodes.Add(nodemodel);
                    diagram.Items.Add(nodemodel);
                }
            }
            if (data.Links != null)
            {
                foreach (var link in data.Links)
                {
                    var source = nodes.FirstOrDefault(p => p.Id == new Guid(link.SourceId));
                    var target = nodes.FirstOrDefault(p => p.Id == new Guid(link.TargetId));
                    var linkmodel = link.ToLinkModel(diagram, source, target);
                    diagram.Items.Add(linkmodel);
                }
            }
        }

        private static DiagramItemViewModel ToNodelModel(this DiagramNode diagramNode, IDiagramViewModel diagram)
        {
            DiagramItemViewModel nodeModel = diagramNode.ToNodel(diagram);

            nodeModel.Id = new Guid(diagramNode.Id);
            if (!string.IsNullOrEmpty(diagramNode.ParentId))
            {
                nodeModel.ParentId = new Guid(diagramNode.ParentId);
            }
            nodeModel.Root = diagram;
            nodeModel.ColorViewModel.FillColor.Color = SerializeHelper.DeserializeColor(diagramNode.Color);
            nodeModel.ColorViewModel.LineColor.Color  = SerializeHelper.DeserializeColor(diagramNode.LineColor);
            nodeModel.FontViewModel.FontColor  = SerializeHelper.DeserializeColor(diagramNode.FontColor);
            nodeModel.FontViewModel.FontSize = diagramNode.FontSize;
            nodeModel.CornerRadius = SerializeHelper.DeserializeCornerRadius(diagramNode.CornerRadius);
            nodeModel.BorderThickness = SerializeHelper.DeserializeThickness(diagramNode.BorderThickness);

            nodeModel.Text = diagramNode.Label;
            nodeModel.Name = diagramNode.Name;
            nodeModel.ItemWidth = diagramNode.Width / ScreenHelper.ScreenScale;
            nodeModel.ItemHeight = diagramNode.Height / ScreenHelper.ScreenScale;
            nodeModel.Left = diagramNode.X / ScreenHelper.ScreenScale;
            nodeModel.Top = diagramNode.Y / ScreenHelper.ScreenScale;
            nodeModel.ZIndex = diagramNode.ZIndex;
            nodeModel.IsInnerConnector = diagramNode.IsInnerConnector;
            if (diagramNode.PortAlignmentList != null)
            {
                nodeModel.ClearConnectors();
                for (int i = 0; i < diagramNode.PortAlignmentList.Count; i++)
                {
                    var connecter = new FullyCreatedConnectorInfo(nodeModel, diagramNode.PortAlignmentList[i].ToEnum<ConnectorOrientation>(), nodeModel.IsInnerConnector);
                    connecter.Ratio = SerializeHelper.DeserializePoint(diagramNode.PortRatio[i]);
                    nodeModel.AddConnector(connecter);
                }
            }

            return nodeModel;
        }

        public static ConnectionViewModel ToLinkModel(this DiagramLink diagramLink, IDiagramViewModel diagram, DiagramItemViewModel sourceNode, DiagramItemViewModel targetNode)
        {
            FullyCreatedConnectorInfo sourceConnectorInfo = sourceNode.Connectors.FirstOrDefault(p => p.Orientation.ToString() == diagramLink.SourcePortAlignment);
            FullyCreatedConnectorInfo sinkConnectorInfo = targetNode.Connectors.FirstOrDefault(p => p.Orientation.ToString() == diagramLink.TargetPortAlignment);
            ConnectionViewModel linkModel = new ConnectionViewModel(diagram, sourceConnectorInfo, sinkConnectorInfo, 
                (DrawMode)Enum.Parse(typeof(DrawMode), diagramLink.PathGenerator?? DrawMode.ConnectingLineSmooth.ToString()),
                (RouterMode)Enum.Parse(typeof(RouterMode), diagramLink.Router ?? RouterMode.RouterNormal.ToString()));
            linkModel.Id = new Guid(diagramLink.Id);
            linkModel.ColorViewModel.LineColor.Color = SerializeHelper.DeserializeColor(diagramLink.Color);
            linkModel.ColorViewModel.LineWidth = diagramLink.Width;
            linkModel.Text = diagramLink.Label;
            linkModel.ZIndex = diagramLink.ZIndex;
    

            if (!string.IsNullOrEmpty(diagramLink.SourceMarkerPath))
            {
                linkModel.ShapeViewModel.SourceMarker = new SharpPath() { Path = diagramLink.SourceMarkerPath, Width = diagramLink.SourceMarkerWidth ?? 10, Height = diagramLink.SourceMarkerWidth ?? 10 };
            }
            if (!string.IsNullOrEmpty(diagramLink.TargetMarkerPath))
            {
                linkModel.ShapeViewModel.SinkMarker = new SharpPath() { Path = diagramLink.TargetMarkerPath, Width = diagramLink.TargetMarkerWidth ?? 10.0, Height = diagramLink.TargetMarkerWidth ?? 10 };
            }
            return linkModel;
        }
        #endregion
    }
}
